Dear Mrs. and Mr. Forum,
many things have changed from GAP-3.1 to GAP-3.2, and there are a few
cases where this means not only extensions but the possibility that a
function will cause strange results in GAP-3.2 although it worked well
in GAP-3.1. Maybe the most nasty case is the behaviour of strings.
As said in the announcement of GAP-3.2, now strings are also lists, thus
'IsList( <str> )' yields 'true' for a string <str>. Suppose you have a
function that shall append a suffix to strings, and whose argument can be
either a string or a list of strings, then in GAP-3.1 it may look like this.
gap> test := function( obj ) > if IsList( obj ) then > return List( obj, y -> ConcatenationString( y, ".suffix" ) ); > else > return ConcatenationString( obj, ".suffix" ); > fi; > end;; gap> test( "nolist" ); "nolist.suffix" gap> test( [ "l", "i", "s", "t" ] ); [ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ]
In GAP-3.2 this function does not work.
gap> test( "nolist" ); Error, Append: <list2> must be a list at Append( res, str ) ... in ConcatenationString( y, ".suffix" ) called from fun( i ) called from List( obj, function ( y ) ... end ) called from test( "nolist" ) called from main loop brk> y; 'n' brk> quit; gap> test( [ "l", "i", "s", "t" ] ); [ "l.suffix", "i.suffix", "s.suffix", "t.suffix" ] gap> [ 'l', 'i', 's', 't' ]; "list" gap> List( last ); [ "l", "i", "s", "t" ]
This is because the string '"nolist"' now is a list, and 'test' tries to
concatenate its entries with the suffix; but the entries of '"nolist"'
aren't strings but characters (which are printed in single quotes).
So a string is equivalent to the list of its characters. On the other
hand, a list of strings is not itself a string; as in GAP-3.1, the function
'List' returns for a string the list of strings containing single letters.
Of course this is not the real problem, the solution is simply to ask for
the more special case of being a string first. But there is a problem, and
as everybody will suspect who read the messages to the GAP forum last week,
it is a problem with empties.
gap> IsList( "" ); IsString( "" ); true true gap> IsList( [] ); IsString( [] ); true true gap> [] = ""; true
(By the way, what should the function 'test' defined above return in case
of input '""' or '[]'? In GAP-3.1 there was a well-defined difference.)
Empty string '""' and empty list '[]' are the same now, with one exception:
In contrary to nonempty lists of characters, the empty list is not converted
into a string, it is printed as '[ ]'. And printing one of these objects
is the problem. Suppose you want to write a program that prints strings
enclosed in '"', and prints other objects as they are printed by 'Print',
in GAP-3.1 this function worked.
gap> MyPrint := function( obj ) > if IsString( obj ) then > Print( "\"", obj, "\"\n" ); > else > Print( obj, "\n" ); > fi; > end;; gap> MyPrint( [] ); "[ ]" gap> MyPrint( "" ); ""
We want the empty list to be printed as '[ ]', and the empty string as '""',
so we must be able to distinguish the two objects. The only solution is to
ask for the information that also GAP-3.2 uses to distinguish them.
This can be done using the function 'TYPE' that returns '"string"' for '""',
and '"list"' for '[]'. Note that this is one of the VERY FEW situations
where an undocumented function like 'TYPE' is of interest for the user,
normally one should avoid using such functions.
gap> MyPrint := function( obj ) > if IsString( obj ) and TYPE( obj ) = "string" then > Print( "\"", obj, "\"\n" ); > else > Print( obj, "\n" ); > fi; > end;; gap> MyPrint( [] ); [ ] gap> MyPrint( "" ); ""
Thomas Breuer
(sam@ernie.math.rwth-aachen.de)